package com.ruoyi.business.impl;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.business.domain.SysAppUser;
import com.ruoyi.business.domain.dto.UserAndTokenDto;
import com.ruoyi.business.domain.vo.GetOpenIdVo;
import com.ruoyi.business.service.ISysAppUserService;
import com.ruoyi.business.service.WXAppletService;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.config.WxBase;
import com.ruoyi.framework.web.domain.SmsCodeAuthenticationToken;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 滕玉静
 * @date 2022/03/11
 * @description 微信小程序Service
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class WXAppletServiceImpl implements WXAppletService {
    @Autowired
    private WxBase wxEntity;
    @Autowired
    private ISysDictDataService dictDataService;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ISysAppUserService userService;
    @Autowired
    private TokenService tokenService;
    @Resource
    private AuthenticationManager authenticationManager;

    /**
     * 获取accesstoken
     *
     * @param appid
     * @param appsecret
     * @return
     * @throws Exception
     */
    @Override
    public String getAccessToken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token";
        String param = "appid=" + wxEntity.getAppId() + "&secret=" + wxEntity.getAppSecret() + "&grant_type=client_credential";
        String result = HttpUtils.sendGet(url, param);
        JSONObject jsonObject = JSONObject.parseObject(result);
        log.info("getAccessToken:::" + jsonObject.toJSONString());
        String access_token = jsonObject.getString("access_token");
        if ("undefined".equals(access_token)) {
            return "";
        } else {
            return access_token;
        }
    }


    /**
     * 小程序通过code取OpenID、UnionID
     *
     * @param code
     * @return
     * @throws Exception
     */
    @Override
    public GetOpenIdVo getOpenIdByCode(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        String param = "appid=" + wxEntity.getAppId() + "&secret=" + wxEntity.getAppSecret() + "&js_code=" + code + "&grant_type=authorization_code";
        String result = HttpUtils.sendGet(url, param);
        JSONObject jsonObject = JSONObject.parseObject(result);
        log.info("小程序授权登录:::" + jsonObject.toJSONString());
        Object errcode = jsonObject.get("errcode");
        GetOpenIdVo javaObject = jsonObject.toJavaObject(GetOpenIdVo.class);
        return javaObject;
    }

    /**
     * 获取6位随机验证码
     *
     * @return
     */
    private String getRandomCode() {
        String randomcode = "";
        // 用字符数组的方式随机
        String model = "0123456789";
        char[] m = model.toCharArray();
        for (int j = 0; j < 6; j++) {
            char c = m[(int) (Math.random() * 10)];
            // 保证六位随机数之间没有重复的
            if (randomcode.contains(String.valueOf(c))) {
                j--;
                continue;
            }
            randomcode = randomcode + c;
        }
        System.err.println("randomcode " + randomcode);
        return randomcode;
    }

    /**
     * 生成小程序二维码
     * 生成顾问/机构邀请码
     *
     * @param page  小程序页面路径
     * @param scene 小程序页面参数
     * @return
     */
    @Override
    public String generateQrCode(String page, String scene, String env) {
        String fileName = null;
        try {
            //生成小程序二维码
            InputStream inputStream = getQrCode(page, scene, null, env);
            byte[] imageByte = toByteArray(inputStream);
            //InputStream转成MultipartFile
            MultipartFile multipartFile = null;//new ConvertToMultipartFile(imageByte, "name.jpg", ".jpg", ".jpg", imageByte.length);
            String filePath = RuoYiConfig.getUploadPath();
            fileName = FileUploadUtils.upload(filePath, multipartFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileName;
    }

    /**
     * 生成小程序二维码
     * 生成小程序带参数形式的二维码，测试消息模板跳转链接使用
     *
     * @param page 小程序页面路径
     * @return
     */


    /**
     * 生成小程序二维码
     * 生成小程序分享海报
     *
     * @param page  小程序页面路径
     * @param scene 小程序页面参数
     * @param width 小程序二维码宽度
     * @return
     */
    public InputStream getQrCode(String page, String scene, Integer width, String env) {
        // 获取token
        //获取本地存储的 accesstoken
        String accessToken = getAccessToken();

        // 生成二维码
        String requestUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;
        Map<String, Object> requestParam = new HashMap<>();
        //最大32个可见字符，只支持数字，大小写英文以及部分特殊字符
        // requestParam.put("scene", "storefrontId=" + storefrontId + "&tableNumber=" + tableNumber);
        //必须是已经发布的小程序存在的页面（否则报错）
        requestParam.put("page", page);
        requestParam.put("scene", scene);
        if (width != null) {
            requestParam.put("width", width);
        }
        //是否需要透明底色,默认false
        requestParam.put("is_hyaline", true);
        // 非正式版本, 需要一下参数
        requestParam.put("check_path", false);
        //获取当前开发环境
        String activeProfile = SpringUtils.getActiveProfile();
  /*      if (StringUtils.isNotBlank(activeProfile) && ActiveProfileConstants.DEV.equals(activeProfile)) {
            //只有本地测试使用开发版，其他环境均使用体验版(正式版未发布)
//            // 开发版
//            requestParam.put("env_version", "develop");
            // 体验版
            requestParam.put("env_version", "trial");
        } else {
            // 体验版
            requestParam.put("env_version", "trial");
        }*/
        //{"errcode":41030,"errmsg":"invalid page rid: 61dbc21e-6ba3e38f-187f6989"} 小程序未发布
        //HttpUtils.sendPost(requestUrl, JSONObject.toJSONString(requestParam));
        requestParam.put("env_version", env);
        InputStream inputStream = null/*com.ruoyi.util.HttpUtils.sendPostGetFile(requestUrl, JSONObject.toJSONString(requestParam))*/;
        return inputStream;
    }

    /**
     * 生成顾问/机构邀请码
     *
     * @param userType   用户登录角色1机构2患者
     * @param invitecode 邀请码
     * @return : void
     * @Author LinGQ
     * @DATE 2022/1/7 0007 11:26
     */
    @Override
    public String generateInviteCode(String invitecode,String env) {
        //todo 2022-03-31扫描邀请码后跳转的页面，现在是小程序首页
        String page = "pages/login/index";
        //scene长度32个字符
        String scene = invitecode;
        return generateQrCode(page, scene,env);
    }

    /**
     * inputStream转byte数组
     *
     * @param input
     * @return
     * @throws IOException
     */
    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

    /**
     * 验证签名
     * 公众号 基本配置-填写服务器配置-使用
     * 小程序 开发管理-消息推送-使用
     *
     * @param signature 微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @return
     */
    @Override
    public boolean checkSignature(String signature, String timestamp, String nonce) {
        log.error("signature:" + signature);
        //该参数在微信公众平台配置 基本配置-填写服务器配置-Token
        String token = "globe_cancer";
        String[] arr = new String[]{token, timestamp, nonce};
        // 将 token、timestamp、nonce 三个参数进行字典序排序
        Arrays.sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;

        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行 sha1 加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToHex(digest);
            log.error("SHA-1签名结果:" + tmpStr);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        content = null;
        // 将 sha1 加密后的字符串可与 signature 对比，标识该请求来源于微信
        return tmpStr != null ? tmpStr.equals(signature.toLowerCase()) : false;
    }

    private String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            //以十六进制输出,2为指定的输出字段的宽度.如果位数小于2,则左端补0
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 小程序授权登录(-系统登录逻辑)
     *
     * @param openid       小程序openid
     * @param unionid      开放平台unionid
     * @param weChatName   微信昵称
     * @param weChatImg    微信头像
     * @param inviteUserId 邀请人userId
     * @param inviteCode   工会邀请码
     * @date: 2022-03-25
     */
    @Override
    public UserAndTokenDto appletLogin(String openid,String phone) {
        //初始化要返回的对象
        //判断当前手机号 是否已存在
        SysAppUser one = userService.lambdaQuery().eq(SysAppUser::getPhonenumber, phone).last("limit 1").one();
        //用户不存在，需要注册
        if (one != null) {
            one.setOpenid(openid);
            userService.updateById(one);
        }else{
             one = userService.lambdaQuery().eq(SysAppUser::getOpenid, openid).last("limit 1").one();
            if (one == null) {
                one = new SysAppUser();
                one.setOpenid(openid);
                userService.save(one);
            }
        }
        return login(openid);
    }


    private UserAndTokenDto login(String phone) {
        Authentication authentication = null;
        try {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager
                    .authenticate(new SmsCodeAuthenticationToken(phone));
        } catch (Exception e) {
            e.printStackTrace();
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        // 生成token
        String token = tokenService.createToken(loginUser);
        UserAndTokenDto userAndTokenDto = new UserAndTokenDto();
        userAndTokenDto.setToken(token);
        SysUser user = loginUser.getUser();
        user.setPassword(null);
        userAndTokenDto.setUser(userService.getById(user.getUserId()));
        return userAndTokenDto;
    }

}
